package TenoriOn.managers
{
	import TenoriOn.WAVEncoder;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	public class SoundManager extends EventDispatcher
	{
		private const _SAMPLE_RATE:Number=44100;
		private const _2PI:Number=2*Math.PI;
		private const _PI:Number=Math.PI;
		private const _1024over2PI:Number=512/_2PI;
		private const _2PI_OVER_SAMPLE_RATE:Number=_2PI/_SAMPLE_RATE;
		
		private var _sound:Sound;
		private var _soundChannel:SoundChannel;
		
		private var _playing:Boolean=false;
		public function get playing():Boolean{return _playing;}
		
		private var _latency:int=0;
		
		private var _sequencerMananger:SequencerManager;
		private var _octaveManager:OctaveManager;
		private var _volumeManager:VolumeManager;
		private var _toneManager:ToneManager;
		private var _gateManager:GateManager;
		
		public static const PLAYING:String="TenoriOn.SoundManager.Playing";
		public static const STOPPED:String="TenoriOn.SoundManager.STOPPED";
		
		public function SoundManager(sequencer:SequencerManager,octave:OctaveManager,volume:VolumeManager,toneManager:ToneManager,gateManager:GateManager){
			_sequencerMananger=sequencer;
			_octaveManager=octave;
			_volumeManager=volume;
			_toneManager=toneManager;
			_gateManager=gateManager;
			_sequencerMananger.soundManager=this;
		}
		
		private function onSample(event:SampleDataEvent):void{
			var sample:Number;
			var notes:Vector.<Vector.<int>>=new Vector.<Vector.<int>>(16);
			for(var j:int=0;j<16;j++){
				notes[j]=new Vector.<int>();
				for(var k:int=0;k<16;k++)
					if(_sequencerMananger._sequence.data[j][k])
						notes[j].push(_octaveManager.scale[k]);
			}
			if(_soundChannel&&event.position)
				_latency=Math.floor((event.position - _soundChannel.position*44.1)/_gateManager.gateLength);
			
			for (var x:Number=0; x<4096; x++){
				sample=0;
				for (var i:int=0;i<notes[_gateManager.gatePosition].length;i++)
					sample+=_toneManager.toneFunction(((event.position+2*x)*notes[_gateManager.gatePosition][i]*_2PI_OVER_SAMPLE_RATE));
				sample=sample*_volumeManager.volume*_gateManager.gateFunction(_gateManager.gateProgress)/(notes[_gateManager.gatePosition].length+1);
				_gateManager.gateProgress+=2;
				
				event.data.writeFloat(sample);
				event.data.writeFloat(sample);
				event.data.writeFloat(sample);
				event.data.writeFloat(sample);
			}
		}
		
		
		public function get position():int{
			var val:int=_gateManager.gatePosition-_latency;
			return val>0?val:15+val;
		}
		
		public function calculateSequence(bitDepth:int=16,sampleRate:int=22050,stereo:Boolean=false):ByteArray{
			var data:ByteArray=new ByteArray();
			data.endian=Endian.LITTLE_ENDIAN;
			var sample:Number;
			var samplesPerNote:int=(sampleRate==44100)?8192:4096;
			var notes:Vector.<Vector.<int>>=new Vector.<Vector.<int>>(16);
			for(var j:int=0;j<16;j++){
				notes[j]=new Vector.<int>();
				for(var k:int=0;k<16;k++)
					if(_sequencerMananger._sequence.data[j][k])
						notes[j].push(_octaveManager.scale[k]);
			}
			var x:int=0;
			do{
				sample=0;
				for (var i:int=0;i<notes[_gateManager.gatePosition].length;i++)
					sample+=_toneManager.toneFunction(x*notes[_gateManager.gatePosition][i]*_2PI_OVER_SAMPLE_RATE);
				sample=32768*sample*_volumeManager.volume*_gateManager.gateFunction(_gateManager.gateProgress)/(notes[_gateManager.gatePosition].length+1);
				_gateManager.gateProgress+=2;
				x+=2;
				data.writeShort(sample);
				if(stereo){
					data.writeShort(sample);
				}
			}while((_gateManager.gatePosition+_gateManager.gateProgress)>0);
			
			_gateManager.resest();
			
			return data
		}
		
		public function play():void{
			if(!_playing){
				_sound=new Sound();
				_sound.addEventListener(SampleDataEvent.SAMPLE_DATA,onSample);
				_soundChannel=_sound.play();
				_playing=true;
				dispatchEvent(new Event(PLAYING));
			}
		}
		public function stop():void{
			if(_playing){
				_soundChannel.stop();
				_playing=false;
				_sound=null;
				_soundChannel=null;
				_gateManager.resest();
				_sequencerMananger.updateGrid();
				dispatchEvent(new Event(STOPPED));
			}
		}
	}
}